#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using AbstractClass.Collections.Generic;

namespace AbstractClass.Collections
{
    /// <summary>
    /// Represents a matrix whose entries are in the Boolean range.
    /// </summary>
    public sealed class BooleanMatrix : Matrix<bool>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BooleanMatrix"/> class.
        /// </summary>
        /// <param name="rowCount">The number of rows.</param>
        /// <param name="columnCount">The number of columns.</param>
        public BooleanMatrix(int rowCount, int columnCount) : base(rowCount, columnCount)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BooleanMatrix"/> class.
        /// </summary>
        /// <param name="matrix">A <see cref="Matrix&lt;Boolean&gt;"/> object whose data will be copied to the new <see cref="BooleanMatrix"/>.</param>
        public BooleanMatrix(Matrix<bool> matrix) : base(matrix.RowCount, matrix.ColumnCount, matrix.GetData())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BooleanMatrix"/> class.
        /// </summary>
        /// <param name="rowCount">The number of rows.</param>
        /// <param name="columnCount">The number of columns.</param>
        /// <param name="data">The data for the new <see cref="BooleanMatrix"/>.</param>
        private BooleanMatrix(int rowCount, int columnCount, bool[] data) : base(rowCount, columnCount, data)
        {
        }

        /// <summary>
        /// Creates a new <see cref="BooleanMatrix"/> coping the data from the specified one.
        /// </summary>
        /// <param name="matrix">The matrix whose data are copied to a new <see cref="BooleanMatrix"/>.</param>
        /// <returns>A new <see cref="BooleanMatrix"/> containing the data of the specified matrix.</returns>
        public new static BooleanMatrix Create(Matrix<bool> matrix)
        {
            var data = new bool[matrix.GetData().LongLength];
            matrix.GetData().CopyTo(data, 0);
            return new BooleanMatrix(matrix.RowCount, matrix.ColumnCount, data);
        }

        /// <summary>
        /// Gets the reflexive closure of this instance.
        /// </summary>
        /// <returns>A new <see cref="BooleanMatrix"/> which is a reflexive closure of this instance.</returns>
        public BooleanMatrix ReflexiveClosure()
        {
            var closure = Clone() as BooleanMatrix;
            if (closure != null)
            {
                for (int x = 0; x < closure.ColumnCount; x++)
                {
                    for (int y = 0; y < closure.RowCount; y++)
                    {
                        if (x == y)
                            closure[y, x] = true;
                    }
                }

                return closure;
            }
            return null;
        }

        /// <summary>
        /// Gets the symmetric closure of this instance.
        /// </summary>
        /// <returns>A new <see cref="BooleanMatrix"/> which is a symmetric closure of this instance.</returns>
        public BooleanMatrix SymmetricClosure()
        {
            var closure = Clone() as BooleanMatrix;
            if (closure != null)
            {
                for (int x = 0; x < closure.ColumnCount; x++)
                {
                    for (int y = 0; y < closure.RowCount; y++)
                    {
                        if (x != y && closure[y, x])
                            closure[x, y] = true;
                    }
                }

                return closure;
            }
            return null;
        }

        /// <summary>
        /// Gets the transitive closure of this instance.
        /// </summary>
        /// <returns>A new <see cref="BooleanMatrix"/> which is a transitive closure of this instance.</returns>
        public BooleanMatrix TransitiveClosure()
        {
            var closure = Clone() as BooleanMatrix;
            if (closure != null)
            {
                for (int n = 0; n < closure.RowCount; n++)
                {
                    for (int x = 0; x < closure.ColumnCount; x++)
                    {
                        for (int y = 0; y < closure.RowCount; y++)
                        {
                            if (!closure[y, x])
                                closure[y, x] = closure[y, n] & closure[n, x];
                        }
                    }
                }

                return closure;
            }
            return null;
        }

        /// <summary>
        /// Gets the Meet of two <see cref="BooleanMatrix"/> instances.
        /// </summary>
        /// <param name="a">First <see cref="BooleanMatrix"/>.</param>
        /// <param name="b">Second <see cref="BooleanMatrix"/>.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Meet of <paramref name="a"/> and <paramref name="b"/>.</returns>
        /// <exception cref="InvalidOperationException">Dimension mismatch between <paramref name="a"/> and <paramref name="b"/>.</exception>
        public static BooleanMatrix Meet(BooleanMatrix a, BooleanMatrix b)
        {
            if (a.RowCount == b.RowCount && a.ColumnCount == b.ColumnCount)
            {
                var newMatrix = new BooleanMatrix(a.RowCount, a.ColumnCount);
                for (int x = 0; x < a.ColumnCount; x++)
                {
                    for (int y = 0; y < a.RowCount; y++)
                    {
                        newMatrix[y, x] = a[y, x] & b[y, x];
                    }
                }
                return newMatrix;
            }
            throw new InvalidOperationException(LanguageResource.BooleanMatrixMeetInvalidMatrices);
        }

        /// <summary>
        /// Gets the Meet of a <see cref="BooleanMatrix"/> and a <see cref="Boolean"/> value.
        /// </summary>
        /// <param name="a">First <see cref="BooleanMatrix"/>.</param>
        /// <param name="value">A Boolean value.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Meet of <paramref name="a"/> and <paramref name="value"/>.</returns>
        public static BooleanMatrix Meet(BooleanMatrix a, bool value)
        {
            var newMatrix = new BooleanMatrix(a.RowCount, a.ColumnCount);
            for (int x = 0; x < a.ColumnCount; x++)
            {
                for (int y = 0; y < a.RowCount; y++)
                {
                    newMatrix[y, x] = a[y, x] & value;
                }
            }
            return newMatrix;
        }

        /// <summary>
        /// Gets the Join of two <see cref="BooleanMatrix"/> instances.
        /// </summary>
        /// <param name="a">First <see cref="BooleanMatrix"/>.</param>
        /// <param name="b">Second <see cref="BooleanMatrix"/>.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Join of <paramref name="a"/> and <paramref name="b"/>.</returns>
        /// <exception cref="InvalidOperationException">Dimension mismatch between <paramref name="a"/> and <paramref name="b"/>.</exception>
        public static BooleanMatrix Join(BooleanMatrix a, BooleanMatrix b)
        {
            if (a.RowCount == b.RowCount && a.ColumnCount == b.ColumnCount)
            {
                var newMatrix = new BooleanMatrix(a.RowCount, a.ColumnCount);
                for (int x = 0; x < a.ColumnCount; x++)
                {
                    for (int y = 0; y < a.RowCount; y++)
                    {
                        newMatrix[y, x] = a[y, x] | b[y, x]; //or = +
                    }
                }
                return newMatrix;
            }
            throw new InvalidOperationException(LanguageResource.BooleanMatrixMeetInvalidMatrices);
        }

        /// <summary>
        /// Gets the Join of a <see cref="BooleanMatrix"/> and a <see cref="Boolean"/> value.
        /// </summary>
        /// <param name="a">First <see cref="BooleanMatrix"/>.</param>
        /// <param name="value">A Boolean value.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Join of <paramref name="a"/> and <paramref name="value"/>.</returns>
        public static BooleanMatrix Join(BooleanMatrix a, bool value)
        {
            var newMatrix = new BooleanMatrix(a.RowCount, a.ColumnCount);
            for (int x = 0; x < a.ColumnCount; x++)
            {
                for (int y = 0; y < a.RowCount; y++)
                {
                    newMatrix[y, x] = a[y, x] | value;
                }
            }
            return newMatrix;
        }

        /// <summary>
        /// Multiplies two <see cref="BooleanMatrix"/> instances.
        /// </summary>
        /// <param name="a">First <see cref="BooleanMatrix"/>.</param>
        /// <param name="b">Second <see cref="BooleanMatrix"/>.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Product of <paramref name="a"/> and <paramref name="b"/>.</returns>
        /// <exception cref="MatrixSizeMismatchException">Number of columns in <paramref name="a"/> is not equal to number of rows in <paramref name="b"/>.</exception>
        public static BooleanMatrix Product(BooleanMatrix a, BooleanMatrix b)
        {
            if (a.ColumnCount == b.RowCount)
            {
                var newMatrix = new BooleanMatrix(a.RowCount, b.ColumnCount);

                for (int j = 0; j < a.RowCount; j++)
                {
                    for (int z = 0; z < b.ColumnCount; z++)
                    {
                        for (int i = 0; i < a.ColumnCount; i++)
                        {
                            bool value1 = a[j, i];
                            bool value2 = b[i, z];

                            newMatrix[j, z] |= (value1 & value2);
                        }
                    }
                }

                return newMatrix;
            }
            throw new MatrixSizeMismatchException(a.RowCount, a.ColumnCount, b.RowCount, b.ColumnCount);
        }

#pragma warning disable 1587
        /// <summary>
        /// Multiplies two <see cref="BooleanMatrix"/> instances.
        /// </summary>
        /// <param name="left">Left <see cref="BooleanMatrix"/>.</param>
        /// <param name="right">Right <see cref="BooleanMatrix"/>.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is Product of <paramref name="left"/> and <paramref name="right"/>.</returns>
        /// <exception cref="InvalidOperationException">Dimension mismatch between <paramref name="left"/> and <paramref name="right"/>.</exception>
#pragma warning restore 1587
        public static BooleanMatrix operator *(BooleanMatrix left, BooleanMatrix right)
        {
            return Product(left, right);
        }

        /// <summary>
        /// Gets the identity <see cref="BooleanMatrix"/>.
        /// </summary>
        /// <param name="count">Number of rows / columns in the identity matrix.</param>
        /// <returns>A <see cref="BooleanMatrix"/> which is an identity matrix.</returns>
        public static BooleanMatrix Identity(int count)
        {
            var m = new BooleanMatrix(count, count);
            for (int i = 0; i < count; ++i)
                m[i, i] = true;
            return m;
        }
    }
}